﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace WebParser
{
    [Obsolete("", true)]
    public class SocketMultipartReceiver
    {
        public event EventHandler<ContentEventArgs> ContentReceived;
        public event EventHandler<ErrorEventArgs> Error;
        protected virtual void OnError(ErrorEventArgs e)
        {
            if (Error != null) Error(this, e);
        }

        public string Host { get; private set; }
        public int Port { get; private set; }
        public string Method { get; private set; }
        public string QueryString { get; private set; }
        public string Result { get; private set; }
        public string[] Header { get { return _header.ToArray(); } }

        public bool IsRunning
        {
            get { return GetSync("IsRunning"); }
            private set { SetSync("IsRunning", value); }
        }
        protected bool KeepRunning
        {
            get { return GetSync("KeepRunning"); }
            set { SetSync("KeepRunning", value); }
        }
        private Socket _socket;
        private Action<byte> _p;
        private readonly List<byte> _tmp;
        private readonly byte[] _buffer;
        private readonly Encoding _enc = Encoding.ASCII;
        private readonly List<string> _header;
        private string _boundary;
        private string _contentType;
        private int _contentLength;

        private readonly List<object> _sync = new List<object>();
        private bool GetSync(object token)
        {
            return _sync.Contains(token);
        }
        private void SetSync(object token, bool enable)
        {
            var contains = _sync.Contains(token);
            if ((enable && contains) || (!enable && !contains)) return;
            if (enable)
                _sync.Add(token);
            else
                _sync.Remove(token);
        }

        public SocketMultipartReceiver(string host, int port, string method, string queryString)
        {
            Host = host;
            Port = port;
            Method = method;
            QueryString = queryString;
            _tmp = new List<byte>();
            _buffer = new byte[0xFFF];
            _header = new List<string>();
        }

        public void Start()
        {
            if (IsRunning) return;
            IsRunning = true;
            KeepRunning = true;
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _socket.Connect(Host, Port);
            new Thread(Receive) { IsBackground = true}.Start();
        }
        private void SwitchTo(Action<byte> function)
        {
            var source = (_p == null) ? "{EMPTY}" : _p.Method.Name;
            var target = (function == null) ? "{EMPTY}" : function.Method.Name;
            //Debug.WriteLine(string.Format("{0} => {1}", source, target));
            _p = function;
        }
        private void ReadResult(byte b)
        {
            if (b == '\r') return;
            if (b == '\n')
            {
                Result = _enc.GetString(_tmp.ToArray());
                _tmp.Clear();
                SwitchTo(ReadHeader);
                return;
            }
            _tmp.Add(b);
        }
        private void ReadHeader(byte b)
        {
            if (b == '\r') return;
            if (b == '\n')
            {
                if (_tmp.Count < 1)
                {
                    SwitchTo(ReadBoundary);
                    return;
                }
                var line = _enc.GetString(_tmp.ToArray());
                if (line.StartsWith("Content-Type"))
                {
                    var index = line.IndexOf("boundary=");
                    if (index > 0)
                        _boundary = "--"+line.Substring(index + 9);
                }
                _header.Add(line);
                _tmp.Clear();
                return;
            }
            _tmp.Add(b);
        }
        private void ReadBoundary(byte b)
        {
            if (b == '\r') return;
            if (b == '\n')
            {
                var line = _enc.GetString(_tmp.ToArray());
                if (line == _boundary)
                    SwitchTo(ReadFrameHeader);
                _tmp.Clear();
                return;
            }
            _tmp.Add(b);
        }
        private void ReadFrameHeader(byte b)
        {

            if (b == '\r') return;
            if (b == '\n')
            {
                if (_tmp.Count < 1)
                {
                    SwitchTo(ReadFrameData);
                    return;
                }
                var line = _enc.GetString(_tmp.ToArray());
                var index = line.IndexOf(": ");
                if (index > -1)
                {
                    var key = line.Substring(0, index);
                    switch (key)
                    {
                        case "Content-Type":
                            _contentType = line.Substring(index + 2);
                            break;
                        case "Content-Length":
                            int len;
                            if (int.TryParse(line.Substring(index + 2), out len))
                                _contentLength = len;
                            break;
                    }
                }
                _tmp.Clear();
                return;
            }
            _tmp.Add(b);
        }

        private Stopwatch _timer = new Stopwatch();
        private void ReadFrameData(byte b)
        {
            _tmp.Add(b);
            if (_tmp.Count < _contentLength) return;
            if (_timer == null) 
                _timer = new Stopwatch();
            else
                Debug.WriteLine(string.Format("{0}", _timer.Elapsed.TotalMilliseconds));
            _timer.Reset();
            _timer.Start();
            if (ContentReceived != null)
            {
                var e = new ContentEventArgs(_tmp.ToArray(), _contentType, _contentLength);
                ContentReceived(this, e);
                _contentType = null;
                _contentLength = -1;
            }
            _tmp.Clear();
            SwitchTo(ReadBoundary);
        }
        private void Receive()
        {
            const string template = @"{METHOD} {QUERY_STRING} {PROTOCOL}

";
            var request =
                Encoding.ASCII.GetBytes(
                    template
                        .Replace("{METHOD}", "GET")
                        .Replace("{QUERY_STRING}", QueryString)
                        .Replace("{PROTOCOL}", "HTTP/1.1"));
            try
            {
                _socket.Send(request);
                SwitchTo(ReadResult);
                while (KeepRunning)
                {
                    var len = _socket.Receive(_buffer, 0, _buffer.Length, SocketFlags.None);
                    for (var i = 0; i < len; i++)
                        _p(_buffer[i]);
                }
            }
            catch (SocketException ex)
            {
                if (KeepRunning) OnError(new ErrorEventArgs(ex));
            }
            catch (Exception ex)
            {
                OnError(new ErrorEventArgs(ex));
            }
            IsRunning = false;
        }
        public void Stop()
        {
            KeepRunning = false;
            _socket.Close(1000);
        }
    }
}